/****************************************************************************
 * Elisa 1.0                                                                *
 *--------------------------------------------------------------------------*
 * Author: Laurent Granvilliers, Vincent Sorin                              *
 * Copyright (c) 2004 Laboratoire d'Informatique de Nantes Atlantique       *
 *--------------------------------------------------------------------------*
 * Elisa is distributed WITHOUT ANY WARRANTY. Read the associated           *
 * COPYRIGHT file for more details.                                         *
 *--------------------------------------------------------------------------*
 * elisa_reduction.h                                                        *
 ****************************************************************************/


/**
 * @file elisa_reduction.h
 * @brief Classes for domain reduction operators
 */

/**
 * @defgroup Reduction Reduction
 * @brief Classes for domain reductions.
 */


#ifndef _ELISA_REDUCTION_H
#define _ELISA_REDUCTION_H

#include <map>
#include <string>
#include <iostream>

#include "elisa_defs.h"
#include "elisa_sortable.h"
#include "elisa_constraint.h"
#include "elisa_reduction_factory.h"

NAMESPACE_ELISA

/**
 * @ingroup Reduction
 * @class Reduction
 * @brief Solver for domain reductions
 *
 * <ul>
 * <li> A reduction is a solver that reduces domains of variables
 *      occurring in a set of constraints
 * <li> Base class Sortable allows sets of reductions to be sorted
 * </ul>
 */
class Reduction : public Sortable, public Observer 
{
public:
	/**
 	 * Default constructor
 	 */
	Reduction() 
	: m_isActive(false)
	{	
	}

	/**
 	 * @brief Constructor
 	 *
 	 * build a reduction given a constraint
 	 * @param c the constraint for the reduction
 	 * @post the reduction is inactive
 	 */
	Reduction(Constraint & c)
	: m_isActive(false)
	{
		m_constraints.push_back(&c);
	}

	/**
 	 * Copy constructor
 	 * @param o the reduction to copy
 	 * @post the reduction is inactive
 	 */
	Reduction(const Reduction & o) 
	: Sortable(o), Observer(o),
	  m_constraints(o.m_constraints),
	  m_isActive(false)
	{
	}

	/**
 	 * Destructor
 	 */
	virtual ~Reduction()
	{
	}

	/// @name Post a constraint in the set of constraint to be processed
	//@{
	void post(Constraint & c)
	{
		m_constraints.push_back(&c);
	}

	void post(Constraint *c)
	{
		if (c != 0) {
			m_constraints.push_back(c);
		}
	}
	//@}

	/**
 	 * Returns true if the reduction is awaken
 	 * @return true if the reduction is awaken
 	 */
	bool isActive() const
	{
		return m_isActive;
	}

	/**
 	 * The reduction becomes awaken following a domain event
 	 *
 	 * it indicates to all uplinked reduction that *this is waking up
 	 */
	void setActive()
	{
		if (!m_isActive) {
			m_isActive = true;
			SetReduction::const_iterator it;
			for (it = m_dependentReductions.begin(); it != m_dependentReductions.end(); ++it) {
				(*it)->notifyActivation(this);
			}
		}
	}

	/**
 	 * The reduction becomes inactive
 	 * @post this->isActive()==false
 	 */
	void setInactive()
	{
		m_isActive = false;
	}

	/**
 	 * To awaken all reductions linked to this reduction
 	 * @post this->isActive()==true
 	 */
	virtual void initActive()
	{
		m_isActive = true;
	}

	/**
 	 * To inactive all reductions linked to this reduction
 	 * @post this->isActive()==false
 	 */
	virtual void initInactive()
	{
		m_isActive = false;
	}

	/**
 	 * Call of the reduction as a listener of domain events => becomes awaken
 	 * @post this->isActive()==true
 	 */
	virtual void call()
	{
		setActive();
	}

	/**
 	 * Reduction o becomes awaken and THIS depends on o.
 	 * This method can be used to update propagation structures
 	 * @param o reduction of which *this depends
 	 */
	virtual void notifyActivation(Reduction *o)
	{
	}

	/**
 	 * Add a reduction to wake up if *this becomes awaken
 	 * @param o reduction that must be called if THIS becomes awaken
 	 */
	void addDependentReduction(Reduction *o)
	{
		if (o != 0) {
			m_dependentReductions.insert(o);
		}
	}

	/// @name Comparisons
	//@{
	virtual bool
	operator==(const Reduction & o) const
	{
		return(this == (&o));
	}

	virtual bool
	operator!=(const Reduction & o) const
	{
		return(this != (&o));
	}
	//@}


	// Method from Sortable
	virtual double priority() const
	{
		return 1.0;
	}

	/**
 	 * Reduction function
 	 * @return false if a domain becomes empty and true otherwise
 	 */
	bool apply()
	{
		setInactive();

		return reductionAlgorithm();
	}

	/**
 	 * Reduction algorithm to be implemented by derived classes
 	 * @return false if a domain becomes empty and true otherwise
 	 */
	virtual bool reductionAlgorithm() = 0;


	virtual string getReductionName() const = 0;

	const VecConstraints *getConstraints() const
	{
		return &m_constraints;
	}

	void printConstraints(std::ostream & os, std::string indent, VecConstraints *vec) const
	{
		int n = 0;

		VecConstraints::const_iterator it, it_num;
		for (it = m_constraints.begin(); it != m_constraints.end(); it++) {
			os << indent << "(";


			n = 0;

			for (it_num = vec->begin(); it_num != vec->end(); it_num++) {
				if ((*it) == (*it_num)) {
					os << n;
					break;
				}
				n++;
			}

			os << ")";

			//(*it)->print(os) ;

			if ((it + 1) != m_constraints.end()) {
				os << ", ";
			}

			n++;
		}
	}

	virtual void print(std::ostream & os, std::string indent, VecConstraints *v) const = 0;


protected:
	// @var False if the reduction must not be applied
	bool m_isActive;
	// @var The set of constraints to be processed by the reduction
	VecConstraints m_constraints;
	// @var Set of reductions to call if THIS becomes awaken
	SetReduction m_dependentReductions;

private:
	/// Copy protection
	Reduction & operator=(const Reduction & o) {}
};

END_NAMESPACE_ELISA

#endif // ELISA_REDUCTION_H
